package QC.MessageBox.handler;

import QC.MessageBox.annotation.MethodLog;
import QC.MessageBox.annotation.RouteMapping;
import QC.MessageBox.beans.*;
import QC.MessageBox.beans.appmsg.BaseAppMsg;
import QC.MessageBox.beans.appmsg.ExtraMsg;
import QC.MessageBox.beans.rcform.RCSingleForm;
import QC.MessageBox.beans.wbmsg.WBTextMsg;
import QC.MessageBox.common.HttpUtils;
import QC.MessageBox.common.PeriodicTime;
import QC.MessageBox.common.WxApiErrorResult;
import QC.MessageBox.common.WxUtils;
import QC.MessageBox.enums.*;
import QC.MessageBox.exceptions.ParamException;
import QC.MessageBox.service.*;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

/**
 * Created by Andaren on 2017/5/9.
 * Used for: 处理融云请求
 */
@RouteMapping(path = "/wx/message")
public class RCHandler extends HandlerBase {

    private static final Logger LOGGER = LoggerFactory.getLogger(RCHandler.class);

    private BlockingService blockingService;

    public RCHandler () {}

    public RCHandler (Map<String, Object> handlerMap) {
        Objects.requireNonNull(handlerMap);
        this.dataAccess = (DataAccess)handlerMap.get("dataAccess");
        this.httpService = (HttpService)handlerMap.get("httpService");
        this.sqlService = (MySQLService)handlerMap.get("mySQLService");
        this.redisService = (RedisService)handlerMap.get("redisService");
//        this.mongoService = (MongoService) handlerMap.get("mongoService");
        this.webClientService = (WebClientService)handlerMap.get("webClientService");
        this.blockingService = (BlockingService)handlerMap.get("blockingService");
    }

    /**
     * 回复微信用户单聊消息
     * @param rt
     */
    @RouteMapping(path = "/back", method = HttpMethod.POST)
    @MethodLog("回复微信用户")
    public void handleMsgBackToWx(RoutingContext rt) {
        HttpServerResponse appResponse = rt.response();

        String requestBodyStr = rt.getBodyAsString();
        LOGGER.info(">>>received app msg -> {}", requestBodyStr);
        // BaseMsg
        BaseAppMsg baseAppMsg = null;
        try {
            baseAppMsg = new BaseAppMsg(requestBodyStr);
            // 设置平台类型为app
            baseAppMsg.setPlateformType(0);
        } catch (UnsupportedEncodingException encodeExcep) {
            LOGGER.info("[{}]参数解码异常,error={}", "回复微信用户", encodeExcep.getMessage(), encodeExcep);
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM_DECODE.getResultJson(encodeExcep.getMessage()));
            return;
        } catch (ParamException pExcep) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM_DECODE.getResultJson(pExcep.getMessage()));
            LOGGER.info("[{}]参数异常,error={}", "回复微信用户", pExcep.getMessage(), pExcep);
            return;
        }

        // 验证请求参数
        if (!checkRequestParam(appResponse, baseAppMsg)) {
            return;
        }
        Future<Void> replyFuture = Future.future();
        replyMsg(baseAppMsg).setHandler(as -> {
            if (as.succeeded()) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.SUCCESS.getResultJson(null));
                LOGGER.info("回复成功");
                replyFuture.complete();
            } else {
                LOGGER.info("回复微信用户失败：[{}]", as.cause().getMessage());
                as.cause().printStackTrace();
                // 粉丝发送超数，返回错误信息
                String causeMsg = as.cause().getMessage();
                if (causeMsg != null && causeMsg.contains("code")) {
                    // [app回复微信用户失败：code[45047]]
                    WxApiErrorResult errorResult = Json.decodeValue(causeMsg, WxApiErrorResult.class);
                    if (45047 == errorResult.getCode()) {
                        HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_WX_MSG.getResultJson("对该粉丝发送消息超限"));
                    } else {
                        HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_HANDLE.getResultJson(as.cause().getMessage()));
                    }
                } else {
                    HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_HANDLE.getResultJson(as.cause().getMessage()));
                }
                replyFuture.fail(causeMsg);
            }
        });
    }

    /**
     * 网页回复微信用户
     * @param rt
     */
    @RouteMapping(path = "/back/web")
    @MethodLog("网页上回应微信用户消息")
    public void handleMsgBackToWxByWeb(RoutingContext rt) {
        HttpServerRequest appRequest = rt.request();
        HttpServerResponse appResponse = rt.response();
        String method = "result";
        String s = "{result:1}";
        // 日常返回
        appResponse
                .putHeader("content-type", "text/html; charset=UTF-8")
                .putHeader("Access-Control-Allow-Origin", "*")
                .end(method+"("+s+")");

        LOGGER.info("[接收到网页回复微信用户请求]");

        // 消息组装
        BaseAppMsg baseAppMsg = new BaseAppMsg();
        baseAppMsg.setOpenId(appRequest.getParam("openId"))
                .setType(appRequest.getParam("type"))
                .setDeveloperId(appRequest.getParam("developerId"))
                .setContent(appRequest.getParam("content"));
        // 验证请求参数
        if (StringUtils.isBlank(baseAppMsg.getOpenId())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("openId"));
            return;
        } else if (StringUtils.isBlank(baseAppMsg.getDeveloperId())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("developerId"));
            return;
        } else if (StringUtils.isBlank(baseAppMsg.getType())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("type"));
            return;
        } else if (StringUtils.isBlank(baseAppMsg.getContent())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("content"));
            return;
        }
        Future<Void> replyFuture = Future.future();
        replyMsg(baseAppMsg).setHandler(as -> {
            if (as.succeeded()) {
                HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.SUCCESS.getResultJson(null));
                LOGGER.info("回复成功");
                replyFuture.complete();
            } else {
                HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_HANDLE.getResultJson(as.cause().getMessage()));
                LOGGER.info("回复微信用户失败：[{}]", as.cause().getMessage());
                as.cause().printStackTrace();
            }
        });
    }

    /**
     * 群发消息给微信用户
     * @param rt
     * @return
     */
    @RouteMapping(path = "/back/group", method = HttpMethod.POST)
    @MethodLog("app群发消息")
    public void handleMsgGroupSend(RoutingContext rt) {
        HttpServerResponse appResponse = rt.response();

        String requestBodyStr = rt.getBodyAsString();
        // URL 解码
        try {
            requestBodyStr = URLDecoder.decode(requestBodyStr, "utf-8"); // 注意是utf-8形式
            LOGGER.info("[群发消息给微信用户]->{}", requestBodyStr);
        } catch (UnsupportedEncodingException e) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM_DECODE.getResultJson(e.getMessage()));
            e.printStackTrace();
            return;
        }
        // 参数验证
        Map<String, String> requestParamMap = null;
        try {
            requestParamMap = HttpUtils.getMapFromLinkedStr(requestBodyStr);
        } catch (Exception e) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM_MAP.getResultJson(e.getMessage()));
            e.printStackTrace();
            return;
        }
        if (requestParamMap == null) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("参数为空"));
            return;
        } else {
            if (StringUtils.isBlank(requestParamMap.get("developerids"))) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("developerids为空"));
                return;
            } else if (StringUtils.isBlank(requestParamMap.get("tousers"))) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("tousers"));
                return;
            } else if (StringUtils.isBlank(requestParamMap.get("type"))) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("type"));
                return;
            } else if (StringUtils.isBlank(requestParamMap.get("content"))) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("content"));
                return;
            }

            JsonArray developerIds = new JsonArray(requestParamMap.get("developerids"));
            JsonArray toUsers = new JsonArray(requestParamMap.get("tousers"));
            if (developerIds == null || toUsers == null ||  developerIds.size() == 0 || toUsers.size() == 0) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("developerids或者tousers为空"));
                return;
            }
            if (developerIds.size() != toUsers.size()) {
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("公众号个数与用户个数不一致"));
                return;
            }
        }
        String type = requestParamMap.get("type");
        String content = requestParamMap.get("content");
        List<String> developerIdsArray = new JsonArray(requestParamMap.get("developerids")).getList();
        List<String> toUsersArray = new JsonArray(requestParamMap.get("tousers")).getList();
        // 组合，聚合成developerId-openids的映射数据
        Map<String, List<String>> devUserMap = new HashMap<>();
        for (int i = 0; i < developerIdsArray.size(); i++) {
            String key = developerIdsArray.get(i);
            if (devUserMap.get(key) != null) {
                devUserMap.get(key).add(toUsersArray.get(i));
            } else {
                List<String> openidList = new ArrayList<>();
                openidList.add(toUsersArray.get(i));
                devUserMap.put(developerIdsArray.get(i), openidList);
            }
        }
        List<Future> sendByDeveloperIdFutList = new ArrayList<>();
        for (String developerId : devUserMap.keySet()) {//--------------
            Future<Void> sendByDeveloperIdFut = Future.future();
            // 3. 回复
            dataAccess.getWXPublicInfoByDeveloperId(developerId)
                    // 获取公众号
                    .compose(pub -> {
                        if (pub != null) {
                            /**
                             * 按照渠道不同，对应不同的返回方法
                             */
                            if (pub.getChannelType() != null && ChannelType.WX.val() == pub.getChannelType()) {
                                // 发送列表
                                List<Future> sendFutList = new ArrayList<>();
                                for (String openId : devUserMap.get(developerId)) {
                                    Future singleSendFut = Future.future();
                                    sendFutList.add(singleSendFut);
                                    dataAccess.checkWebUser(openId)
                                            .compose(checkOp -> {
                                                if (checkOp.isPresent()) {
                                                    // 发送网页回复
                                                    BaseAppMsg appMsg = new BaseAppMsg();
                                                    appMsg.setContent(content)
                                                            .setDeveloperId(developerId)
                                                            .setType(type)
                                                            .setOpenId(openId);
                                                    // 消息发送
                                                    sendWebReplyForV2(appMsg, pub).setHandler(singleSendFut.completer());
                                                } else {
                                                    // 发送微信回复
                                                    getAccessTokenIfExpired(pub.getDeveloperId())
                                                            .compose(token -> {
                                                                // 如果群发有多媒体资源的消息，先上传多媒体资源
                                                                Future<String> mediaFut = Future.future();
                                                                if (MsgType.IMAGE.getType().equals(type)) {
                                                                    // 涉及到Media_id字段的回复，需要先上传资源
                                                                    blockingService.createFileFromUrl(content)
                                                                            .compose(fileRes ->
                                                                                    httpService.getMediaIdByPostMediaToWX(token, type, fileRes)
                                                                            )
                                                                            .compose(mediaId ->
                                                                                            mediaFut.complete(mediaId),
                                                                                    mediaFut);
                                                                } else if (MsgType.TEXT.getType().equals(type)) { // 文本消息不处理
                                                                    mediaFut.complete(null);
                                                                }
                                                                mediaFut
                                                                        .compose(mediaId -> {
                                                                            BaseAppMsg appMsg = new BaseAppMsg();
                                                                            appMsg.setContent(content)
                                                                                    .setDeveloperId(developerId)
                                                                                    .setType(type)
                                                                                    .setOpenId(openId);
                                                                            // 消息发送
                                                                            send(appMsg, token, mediaId).setHandler(singleSendFut.completer());
                                                                        }, singleSendFut);
                                                            }, singleSendFut);
                                                }
                                            }, singleSendFut);
                                }
                                CompositeFuture.all(sendFutList)
                                        .compose(allSended -> {
                                            sendByDeveloperIdFut.complete();
                                        }, sendByDeveloperIdFut);
                            } else if (pub.getChannelType() != null && ChannelType.WB.val() == pub.getChannelType()) {
                                List<Future> sendFutList = new ArrayList<>();
                                for (String openId : devUserMap.get(developerId)) {
                                    BaseAppMsg appMsg = new BaseAppMsg();
                                    appMsg.setContent(content)
                                            .setDeveloperId(developerId)
                                            .setType(type)
                                            .setOpenId(openId);
                                    // 消息发送
                                    sendFutList.add(sendWBMsg(appMsg, pub.getAccessToken()));
                                }
                                CompositeFuture.all(sendFutList)
                                        .setHandler(allSendAr -> {
                                           if (allSendAr.succeeded()) {
                                               sendByDeveloperIdFut.complete();
                                           } else {
                                               LOGGER.error("群发消息失败");
                                               sendByDeveloperIdFut.fail(allSendAr.cause());
                                           }
                                        });
                            } else {
                                LOGGER.info("error channelType[{}]", pub.getChannelType());
                            }
                        } else {
                            sendByDeveloperIdFut.fail("公众号信息获取失败");
                        }
                    }, sendByDeveloperIdFut);
            sendByDeveloperIdFutList.add(sendByDeveloperIdFut);
        }
        Future<?> groupMsgFut = Future.future();
        CompositeFuture.all(sendByDeveloperIdFutList)
                .compose(ar -> {
                    groupMsgFut.complete();
                }, groupMsgFut);
        groupMsgFut.setHandler(ar -> {
            if (ar.succeeded()) {
                LOGGER.info("群回复成功");
                HttpUtils.commonJsonResponse(appResponse, SendResult.SUCCESS.getResultJson(null));
            } else {
                LOGGER.info("群回复失败：[{}]", ar.cause().getMessage());
                ar.cause().printStackTrace();
                HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_HANDLE.getResultJson(ar.cause().getMessage()));
            }
        });
    }

    @RouteMapping(path = "/web/allchannel/back")
    @MethodLog("回复全渠道用户")
    public void replyAllChannelUser (RoutingContext rt) {

        HttpServerRequest appRequest = rt.request();
        HttpServerResponse appResponse = rt.response();
        String method = "result";
        String s = "{result:1}";
        // 日常返回
        appResponse
                .putHeader("content-type", "text/html; charset=UTF-8")
                .putHeader("Access-Control-Allow-Origin", "*")
                .end(method+"("+s+")");

        LOGGER.info("[接收到网页回复全渠道用户请求]");

        // 消息组装
        BaseAppMsg baseAppMsg = new BaseAppMsg();
        baseAppMsg.setOpenId(appRequest.getParam("openId"))
                .setType(appRequest.getParam("type"))
                .setDeveloperId(appRequest.getParam("developerId"))
                .setContent(appRequest.getParam("content"))
                .setExtra(appRequest.getParam("extra"));
        // 设置平台类型为web
        baseAppMsg.setPlateformType(1);

        // 验证请求参数
        if (StringUtils.isBlank(baseAppMsg.getOpenId())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("openId"));
            return;
        } else if (StringUtils.isBlank(baseAppMsg.getDeveloperId())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("developerId"));
            return;
        } else if (StringUtils.isBlank(baseAppMsg.getType())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("type"));
            return;
        } else if (StringUtils.isBlank(baseAppMsg.getContent())) {
            HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_PARAM.getResultJson("content"));
            return;
        }
        Future<Void> replyFuture = Future.future();
        replyMsg(baseAppMsg).setHandler(as -> {
            if (as.succeeded()) {
                HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.SUCCESS.getResultJson(null));
                LOGGER.info("回复成功");
                replyFuture.complete();
            } else {
                HttpUtils.webTalkJsonResponse(appResponse, method, SendResult.FAILED_HANDLE.getResultJson(as.cause().getMessage()));
                LOGGER.info("回复全渠道用户失败：[{}]", as.cause().getMessage());
                as.cause().printStackTrace();
            }
        });


    }

    /**
     * 消息回复操作
     * @param appMsg
     * @return
     */
    public Future<Void> replyMsg(BaseAppMsg appMsg) {
        Future<Void> replyFuture = Future.future();
        // 3. 回复
        dataAccess.getWXPublicInfoByDeveloperId(appMsg.getDeveloperId())
                // 获取公众号
                .compose(pub -> {
                    if (pub != null) {
                        /**
                         * 按照渠道不同，对应不同的返回方法
                         */
                        if (pub.getChannelType() != null && ChannelType.WX.val() == pub.getChannelType()) {
                            dataAccess.checkWebUser(appMsg.getOpenId())
                                    .compose(checkOp -> {
                                        if (checkOp.isPresent()) {
                                            // 发送网页回复
                                            sendWebReplyForV2(appMsg, pub)
                                                    .compose(msgSended -> {
                                                        LOGGER.info("处理网页回复消息完成]");
                                                        replyFuture.complete();
                                                    }, replyFuture);
                                        } else {
                                            // 发送微信回复
                                            getAccessTokenIfExpired(pub.getDeveloperId())
                                                    .compose(token ->
                                                            send(appMsg, token, null)
                                                    )
                                                    .compose(record -> {
                                                        LOGGER.info("[微信消息保存成功:{}]", record);
                                                        replyFuture.complete();
                                                    }, replyFuture);
                                        }
                                    }, replyFuture);
                        } else if (pub.getChannelType() != null && ChannelType.WB.val() == pub.getChannelType()) {
                            // 发送微博回复
                            sendWBMsg(appMsg, pub.getAccessToken())
                                    .compose(msgSended -> {
                                        LOGGER.info("[微博消息保存成功]");
                                        replyFuture.complete();
                                    }, replyFuture);
                        } else {
                            LOGGER.info("error channelType[{}]", pub.getChannelType());
                        }
                    } else {
                        replyFuture.fail("公众号信息获取失败");
                    }
                }, replyFuture);
        return replyFuture;
    }

    /**
     * 发送单聊消息
     * @param appMsg
     * @param accessToken
     * @param mediaId       // 有媒体资源时，不需要重新上传
     * @return
     */
    public Future<MsgRecord> send(BaseAppMsg appMsg, String accessToken, String mediaId) {
        // 获取消息内容
        Future<String> contentFut = Future.future();
        if("image".equals(appMsg.getType())) {
            if (mediaId != null) {
                contentFut.complete(mediaId);
            } else {
                // 涉及到Media_id字段的回复，需要先上传资源
                blockingService.createFileFromUrl(appMsg.getContent())
                        .compose(fileRes ->
                                httpService.getMediaIdByPostMediaToWX(accessToken, appMsg.getType(), fileRes)
                        )
                        .compose(mediaIdAr ->
                                contentFut.complete(mediaIdAr), contentFut);
            }
        } else if ("text".equals(appMsg.getType())) {
            contentFut.complete(appMsg.getContent());
        } else {
            contentFut.fail("不支持的消息数据类型");
        }

        // 发送消息
        Future<MsgRecord> sendMsgFuture = Future.future();
        contentFut.compose(content -> {
                    JsonObject replyJson = WxUtils.buildReplyJsonMsg(appMsg.getOpenId(), appMsg.getType(), content);
                    Future<String> sendResultFut = webClientService.postReplyMsg2WxUser(replyJson, accessToken);
                    Future<AppUser> appUserFuture = dataAccess.getAppUser(appMsg.getOpenId());
                    return CompositeFuture.all(sendResultFut, appUserFuture);
                })
                .compose(compositeFuture1 -> {
                    AppUser appUser = compositeFuture1.resultAt(1);
                    if (appUser == null) {
                        sendMsgFuture.fail("appUser获取失败");
                    }

                    // 保存消息
                    MsgRecord msgRecord = new MsgRecord();
                    msgRecord.setOpenId(appMsg.getOpenId())
                            .setMsgType(appMsg.getType())
                            .setSourceType(MsgRecord.MSG_FROM_APP)
                            .setContent(Json.encode(appMsg))
                            .setCsId(appUser.getCsId())
                            .setCsNickName(appUser.getNickname())
                            .dropEmoji()
                            .setIsRead("0")
                            .setSendtime(PeriodicTime.getCurrentTimestamp());
                    Future<Void> saveRecFut = Future.future();
                    sqlService.save(msgRecord)
                            .compose(msg -> {
                                // 更新粉丝最近一条消息
                                Future<Void> updateLimitFut =  sqlService.updateUserLimit(msg.getOpenId(),
                                        new EnumMap<ActiveLimit, Long>(ActiveLimit.class) {
                                            {
                                                put(ActiveLimit.LATELY_MSG_ID, msg.getId());
                                            }
                                        });
                                // 更新客服服务数据
                                Future<Void> updateCusServDataActCountFut = sqlService
                                        .updateCusServDataStateOrCountByOpenId(appMsg.getOpenId(), true);
                                return CompositeFuture.all(updateLimitFut, updateCusServDataActCountFut);
                            })
                            .compose(afterSendCompositeAr -> {
                                synchronizedRongCloudMsg(appMsg, appUser, appMsg.getPlateformType(), ChannelType.WX).setHandler(saveRecFut.completer());
                            }, saveRecFut);
                    // 处理完成
                    saveRecFut
                            .compose((Void) ->
                                sendMsgFuture.complete(msgRecord)
                            , sendMsgFuture);
                }, sendMsgFuture);
        return sendMsgFuture;
    }

    /**
     * 不同平台下客服消息时，通过融云同步客服消息
     * @param appMsg
     * @param appUser
     * @param plateformType [0：app端，1：web端]
     * @param channelType   渠道类型[0:微信，1：微博，2：web ]
     * @return
     */
    public Future<Void> synchronizedRongCloudMsg(BaseAppMsg appMsg, AppUser appUser, Integer plateformType, ChannelType channelType) {
        return Future.future(sendFut -> {
            /**
             * 为pc端，手机端客服发送同步，
             * 发送客户信息到融云
             */
            RCSingleForm singleForm = new RCSingleForm();
            final ExtraMsg extraMsg;
            if (appMsg.getExtra() != null) {
                // 生成消息数据
                extraMsg = Json.decodeValue(appMsg.getExtra(), ExtraMsg.class);
                extraMsg.setDeveloperId(appMsg.getDeveloperId())
                        .setOpenId(appMsg.getOpenId())// 暂时无用
                        .setType(SourceType.WX.val())
                        .setChannelType(channelType.val())
                        .setFunctionType(1) // 同步消息标志
                        .setPlateformType(plateformType)// 客服发送消息时所处的平台类型
                        .setTimestamp(String.valueOf(PeriodicTime.getCurrentTimestamp()));
            } else {
                extraMsg = new ExtraMsg();
            }
            /**
             * 根据openid查询用户信息和公众号名称
             */
            Future<WXUser> wxUserFut = dataAccess.findWxUserByOpenId(appMsg.getOpenId());
            Future<WXPublic> pubFut = dataAccess.getWXPublicInfoByDeveloperId(appMsg.getDeveloperId());
            Future<CustomerServ> randomCsFut = dataAccess.getCSInfoByRandom();
            CompositeFuture.all(wxUserFut, pubFut, randomCsFut)
            .compose(compositeFutureAr -> {
                WXUser wxUser = compositeFutureAr.resultAt(0);
                WXPublic wxPub = compositeFutureAr.resultAt(1);
                CustomerServ randomCs = compositeFutureAr.resultAt(2);
                // 将粉丝信息和公众号信息填充到extra中
                if (extraMsg.getPublicName() == null) {
                    extraMsg
                            .setFunctionType(1) // 同步消息标志
                            .setPlateformType(appMsg.getPlateformType())
                            .setHeadUrl(wxUser.getHeadUrl())
                            .setDeveloperId(appMsg.getDeveloperId())
                            .setPublicName(wxPub.getName())
                            .setNickName(wxUser.getWxNickName())
                            .setType(SourceType.WX.val())
                            .setChannelType(channelType.val())
                            .setOpenId(appMsg.getOpenId())
                            .setTimestamp(String.valueOf(PeriodicTime.getCurrentTimestamp()));
                }
                Future<RCSingleForm> msgBuildFut = Future.future();
                if ("text".equals(appMsg.getType())) { // 文本消息
                    JsonObject msgJson = new JsonObject();
                    msgJson.put("content", appMsg.getContent())
                            .put("extra", Json.encode(extraMsg));

                    singleForm.setObjectName(WeiBoHandler.RCMsgType.TEXT.val())
                            .setContent(msgJson.encode());
                    msgBuildFut.complete(singleForm);
                } else if ("image".equals(appMsg.getType())) {  // 图片消息
                    blockingService.getImageInfoByUrl(appMsg.getContent())
                            .compose(imageInfo -> {
                                JsonObject msgJson = new JsonObject();
                                msgJson.put("content", imageInfo.toJson().encode())
                                        .put("imageUri", appMsg.getContent())
                                        .put("extra", Json.encode(extraMsg));
                                singleForm.setObjectName(WeiBoHandler.RCMsgType.IMAGE.val())
                                        .setContent(msgJson.encode());
                                msgBuildFut.complete(singleForm);
                            }, msgBuildFut);
                } else {
                    msgBuildFut.fail("不能处理消息类型 : " + appMsg.getType());
                }
                singleForm.setFromUserId(randomCs.getTalkId())
                        .setToUserId(appUser.getTalkId());
                return msgBuildFut;
            }).compose(singleFormMsg -> {
                sendSingMsgToRC(singleFormMsg, sendFut);
            }, sendFut);
//            sendFut.complete();
        });
    }

    /**
     * 发送微博回复
     * @param appMsg
     * @param token
     * @return
     */
    public Future<Void> sendWBMsg(BaseAppMsg appMsg , String token) {
        final WBTextMsg replyWBMsg = new WBTextMsg();
        replyWBMsg.setReceiver_id(Long.valueOf(appMsg.getOpenId()))
                .setData(appMsg.getContent())
                .setType(MsgType.TEXT.getType())
                .setAccess_token(token);
        return Future.future(msgFut -> {
            // 查询客服信息
            Future<AppUser> appUserFuture = dataAccess.getAppUser(appMsg.getOpenId());
            // 发送回复
            Future<Void> sendFut = webClientService.sendMsgToFans(token, replyWBMsg);
            CompositeFuture.all(appUserFuture, sendFut)
                    .compose(compositeFuture -> {
                        AppUser appUser = compositeFuture.resultAt(0);
                        // 保存消息
                        MsgRecord msgRecord = new MsgRecord();
                        msgRecord.setOpenId(replyWBMsg.getReceiver_id() + "")
                                .setMsgType(replyWBMsg.getType())
                                .setSourceType(MsgRecord.MSG_FROM_APP)
                                .setContent(Json.encode(appMsg))
                                .setCsId(appUser.getCsId())
                                .setCsNickName(appUser.getNickname())
                                .dropEmoji()
                                .setIsRead("0")
                                .setSendtime(PeriodicTime.getCurrentTimestamp());
                        sqlService.save(msgRecord)
                                .compose(msg -> {
                                    // 更新粉丝最近一条消息
                                    Future<Void> updateLimitFut =  sqlService.updateUserLimit(msg.getOpenId(),
                                            new EnumMap<ActiveLimit, Long>(ActiveLimit.class) {
                                                {
                                                    put(ActiveLimit.LATELY_MSG_ID, msg.getId());
                                                }
                                            });
                                    // 更新客服服务数据
                                    Future<Void> updateCusServDataActCountFut = sqlService
                                            .updateCusServDataStateOrCountByOpenId(appMsg.getOpenId(), true);
                                    return CompositeFuture.all(updateLimitFut, updateCusServDataActCountFut);
                                })
                                .compose(car -> {
                                    synchronizedRongCloudMsg(appMsg, appUser, appMsg.getPlateformType(), ChannelType.WB).setHandler(msgFut.completer());
                                }, msgFut);
                    }, msgFut);
        });
    }

    /**
     * 回复web渠道
     * @param appMsg
     * @return
     */
    public Future<Void> sendWebReply(BaseAppMsg appMsg) {
        return Future.future(sendFut -> {
                    Future<CustomerServ> randomCsFut = dataAccess.getCSInfoByRandom();
                    randomCsFut.compose(randomCs -> {
                        Future<RCSingleForm> formMsgFut = Future.future();
                        RCSingleForm singleForm = new RCSingleForm();
                        final ExtraMsg extraMsg;
                        if (appMsg.getExtra() != null) {
                            // 生成消息数据
                            extraMsg = Json.decodeValue(appMsg.getExtra(), ExtraMsg.class);
                            extraMsg.setDeveloperId(appMsg.getDeveloperId())
                                    .setOpenId(appMsg.getOpenId())// 暂时无用
                                    .setType("web")
                                    .setTimestamp(String.valueOf(PeriodicTime.getCurrentTimestamp()));
                        } else {
                            extraMsg = new ExtraMsg();
                        }
                        if ("text".equals(appMsg.getType())) { // 文本消息
                            JsonObject msgJson = new JsonObject();
                            msgJson.put("content", appMsg.getContent())
                                    .put("extra", Json.encode(extraMsg));

                            singleForm.setObjectName(WeiBoHandler.RCMsgType.TEXT.val())
                                    .setContent(msgJson.encode());
                            formMsgFut.complete(singleForm);
                        } else if ("image".equals(appMsg.getType())) {  // 图片消息
                            blockingService.getImageInfoByUrl(appMsg.getContent())
                                    .compose(imageInfo -> {
                                        JsonObject msgJson = new JsonObject();
                                        msgJson.put("content", imageInfo.toJson().encode())
                                                .put("imageUri", appMsg.getContent())
                                                .put("extra", Json.encode(extraMsg));
                                        singleForm.setObjectName(WeiBoHandler.RCMsgType.IMAGE.val())
                                                .setContent(msgJson.encode());
                                        formMsgFut.complete(singleForm);
                                    }, formMsgFut);
                        } else {
                            formMsgFut.fail("不能处理消息类型 : " + appMsg.getType());
                        }
                        singleForm.setFromUserId(randomCs.getTalkId())
                                .setToUserId(appMsg.getOpenId());
                        return formMsgFut;
                    }).compose(singleFormMsg -> {
//                        Future<Void> replyFansFut = Future.future();
//                        Future<Void> notifyCSFut = Future.future();
//                        // 需要网页粉丝需要绑定客服才能同步消息
//                        synchronizedRongCloudMsg(appMsg, appUser, 0);
                        sendSingMsgToRC(singleFormMsg, sendFut);
                    }, sendFut);
                });

    }

    /**
     * 回复web渠道V2
     * @param appMsg
     * @return
     */
    public Future<Void> sendWebReplyForV2(BaseAppMsg appMsg, WXPublic wxPub) {
        return Future.future(sendFut -> {
            Future<AppUser> appUserFuture = dataAccess.getAppUser(appMsg.getOpenId());
            Future<WXUser> wxUserFuture = dataAccess.findWxUserByOpenId(appMsg.getOpenId());
            CompositeFuture.all(appUserFuture, wxUserFuture)
                    .compose(allNeedsCompletedAr -> {
                        AppUser appUser = allNeedsCompletedAr.resultAt(0);
                        WXUser webUser = allNeedsCompletedAr.resultAt(1);

                        Future<RCSingleForm> formMsgFut = Future.future();
                        RCSingleForm singleForm = new RCSingleForm();
                        final ExtraMsg extraMsg = new ExtraMsg();
                        extraMsg.setDeveloperId(appMsg.getDeveloperId())
                                .setOpenId(appMsg.getOpenId())// 暂时无用
                                .setType(SourceType.WX.val())
                                .setChannelType(ChannelType.WEB.val())
                                .setNickName(webUser.getWxNickName())
                                .setHeadUrl(webUser.getHeadUrl())
                                .setPublicName(wxPub.getName())
                                .setTimestamp(String.valueOf(PeriodicTime.getCurrentTimestamp()));
                        if ("text".equals(appMsg.getType())) { // 文本消息
                            JsonObject msgJson = new JsonObject();
                            msgJson.put("content", appMsg.getContent())
                                    .put("extra", Json.encode(extraMsg));

                            singleForm.setObjectName(WeiBoHandler.RCMsgType.TEXT.val())
                                    .setContent(msgJson.encode());
                            formMsgFut.complete(singleForm);
                        } else if ("image".equals(appMsg.getType())) {  // 图片消息
                            blockingService.getImageInfoByUrl(appMsg.getContent())
                                    .compose(imageInfo -> {
                                        JsonObject msgJson = new JsonObject();
                                        msgJson.put("content", imageInfo.toJson().encode())
                                                .put("imageUri", appMsg.getContent())
                                                .put("extra", Json.encode(extraMsg));
                                        singleForm.setObjectName(WeiBoHandler.RCMsgType.IMAGE.val())
                                                .setContent(msgJson.encode());
                                        formMsgFut.complete(singleForm);
                                    }, formMsgFut);
                        } else {
                            formMsgFut.fail("不能处理消息类型 : " + appMsg.getType());
                        }
                        singleForm.setFromUserId(appUser.getTalkId())
                                .setToUserId(webUser.getUnionid());
                        return formMsgFut
                                .compose(singleFormMsg -> {
                                    Future<Void> sendToFansFut = Future.future();
                                    sendSingMsgToRC(singleFormMsg, sendToFansFut);
                                    return sendToFansFut;
                                })
                                .compose(compositeFuture -> {
                                    // 保存消息
                                    MsgRecord msgRecord = new MsgRecord();
                                    msgRecord.setOpenId(appMsg.getOpenId())
                                            .setMsgType(appMsg.getType())
                                            .setSourceType(MsgRecord.MSG_FROM_APP)
                                            .setContent(Json.encode(appMsg))
                                            .setCsId(appUser.getCsId())
                                            .setCsNickName(appUser.getNickname())
                                            .dropEmoji()
                                            .setIsRead("0")
                                            .setSendtime(PeriodicTime.getCurrentTimestamp());
                                    sqlService.save(msgRecord)
                                            .compose(msg -> {
                                                // 更新粉丝最近一条消息
                                                Future<Void> updateLimitFut = sqlService.updateUserLimit(msg.getOpenId(),
                                                        new EnumMap<ActiveLimit, Long>(ActiveLimit.class) {
                                                            {
                                                                put(ActiveLimit.LATELY_MSG_ID, msg.getId());
                                                            }
                                                        });
                                                // 更新客服服务数据
                                                Future<Void> updateCusServDataActCountFut = sqlService
                                                        .updateCusServDataStateOrCountByOpenId(appMsg.getOpenId(), true);
                                                return CompositeFuture.all(updateLimitFut, updateCusServDataActCountFut);
                                            })
                                            .compose(car -> {
                                                synchronizedRongCloudMsg(appMsg, appUser, appMsg.getPlateformType(), ChannelType.WEB)
                                                        .setHandler(sendFut.completer());
                                            }, sendFut);
                                }, sendFut);
                    });
        });

    }

    /**
     * 检验请求参数
     * @param appResponse
     * @param baseAppMsg
     * @return
     */
    public boolean checkRequestParam(HttpServerResponse appResponse, BaseAppMsg baseAppMsg) {
        if (StringUtils.isBlank(baseAppMsg.getOpenId())) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("openId为空"));
            return false;
        } else if (StringUtils.isBlank(baseAppMsg.getDeveloperId())) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("developerId为空"));
            return false;
        } else if (StringUtils.isBlank(baseAppMsg.getType())) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("type为空"));
            return false;
        } else if (StringUtils.isBlank(baseAppMsg.getContent())) {
            HttpUtils.commonJsonResponse(appResponse, SendResult.FAILED_PARAM.getResultJson("content为空"));
            return false;
        }
        return true;
    }
}
